import requests
import base64
import time
import cv2
from threading import Thread
import os
import random
import json
import RPi.GPIO as GPIO
import time
import socket
from screen import Screen


def carDetect(rootPath):
    cap = cv2.VideoCapture(0)  # 0为默认，1为第二个
    width = 512  # 定义摄像头获取图像宽度
    height = 512  # 定义摄像头获取图像长度
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)  # 设置宽度
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)  # 设置长度
    print(">> 摄像头打开：", cap.isOpened())
    while True:
        ret, frame = cap.read()  # 窗口名frame
        cv2.imshow('image', frame)
        cv2.waitKey(1)
        continue

    cap.release()  # 释放摄像头
    cv2.destroyWindow('frame')  # 删除窗口


# 向服务器上传图片
def uploadImg(filepath):
    url = remote_server+'upload_file.php?opt=add'
    filename = filepath.split('/')[-1]
    if filename:
        files = {"file": (filename, open(filepath, "rb"), "image/png")}
        requests.post(url, files=files)
        print(">> 上传完成！")


def deleteCloudImg(filename):
    url = remote_server+'upload_file.php?opt=del&name=' + filename
    if filename:
        requests.get(url)
        print(">> 删除完成！")


def sendImgByCoolPush(imgName,target='1061700625'):
    imgUrl = remote_server+'images/' + imgName
    msgUrl = 'http://xfxuezhang.cn:9966/QQ/send?target={}&msg='.format(target)
    url = msgUrl + imgUrl
    try:
        requests.get(url)
    except:
        pass


capture_flag = {'step': 0, 'path': ''}

def takeCapture():
    global capture_flag
    capture_flag['step'] = 1
    while capture_flag['step'] != 0:
        time.sleep(1)
    return capture_flag['path']


# 人脸识别并截图（这里是主要入口函数）
def faces_video():
    global capture_flag, captureCount
    """人脸识别并截图（这里是主要入口函数）"""
    haarcascade_path = r'data/haarcascades/haarcascade_frontalface_alt.xml'
    face_cascade = cv2.CascadeClassifier(haarcascade_path)  # 获取训练好的人脸的参数数据
    load_succeed = face_cascade.load(haarcascade_path)
    print('load haarcascade: ', load_succeed)  # 训练数据文件是否导入成功
    cap = cv2.VideoCapture(0)  # 0为默认，1为第二个
    width = 256  # 定义摄像头获取图像宽度
    height = 256  # 定义摄像头获取图像长度
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)  # 设置宽度
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)  # 设置长度
    print(">> 摄像头打开：", cap.isOpened())
    # last_time = ''
    last_time = int(time.time())  # s
    while True:
        ret, frame = cap.read()  # 读取1帧摄像头图像
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 图像矩阵
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1,
                                              minNeighbors=10,
                                              flags=cv2.CASCADE_DO_CANNY_PRUNING,  # cv2.CASCADE_SCALE_IMAGE,
                                              minSize=(20, 20)
                                              )
        if capture_flag['step'] == 1:
            imgName = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
            imgPath = os.getcwd() + '/img/' + imgName + '.jpg'
            cv2.imwrite(imgPath, frame)  # 保存图像
            uploadImg(imgPath)
            capture_flag['step'] = 0
            capture_flag['path'] = imgName + '.jpg'

        if len(faces) > 0:
            print('faces: ', len(faces))
            for x, y, w, h in faces:
                cv2.rectangle(frame, (x, y), (x + w, y + w), (0, 255, 0), 2)  # 画矩形
            curr_time = int(time.time())  # s
            if curr_time - last_time > 2:
                screen.openScreen()
                cap.release()  # 释放摄像头
                servorShakeHead()
                captureCount += 1
                updateCaptureCount(captureCount)
                
                last_time = curr_time
                imgName = time.strftime("%Y-%m-%d_%H-%M-%S", time.localtime())
                imgPath = os.getcwd() + '/img/' + imgName + '.jpg'
                cv2.imwrite(imgPath, frame)  # 保存图像
                print(imgName + " => 捕获到人脸，已保存照片")
                cap = cv2.VideoCapture(0)  # 0为默认，1为第二个
                width = 256  # 定义摄像头获取图像宽度
                height = 256  # 定义摄像头获取图像长度
                cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)  # 设置宽度
                cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)  # 设置长度
                print(">> 摄像头重新打开：", cap.isOpened())
                
                uploadImg(imgPath)
                sendImgByCoolPush(imgName + '.jpg')  # send by coolpush
                screen.closeScreen()

        cv2.imshow('frame', frame)  # 显示图像
        key = cv2.waitKey(1) & 0xFF  # 等待键盘输入，延时为毫秒级
        if key == ord('q'):  # 按q退出
            break
    cap.release()  # 释放摄像头
    cv2.destroyWindow('frame')  # 删除窗口


def GpioInit():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(26, GPIO.OUT)  # LED
    GPIO.output(26, GPIO.LOW)
    GPIO.setup(12, GPIO.OUT)  # BEEP
    GPIO.output(12, GPIO.LOW)


def setupSERVO():
    global pwm
    P_SERVO = servor_pin  # GPIO端口号，根据实际修改
    fPWM = 50  # Hz (软件PWM方式，频率不能设置过高)
    GPIO.setup(P_SERVO, GPIO.OUT)
    pwm = GPIO.PWM(P_SERVO, fPWM)
    pwm.start(0)


def setDirection(direction):
    global pwm
    global servor_degree_now
    if direction<0 or direction>180:
        return
    servor_degree_now = direction
    duty = 10 / 180 * direction + 2
    pwm.ChangeDutyCycle(duty)
    print("direction =", direction, "-> duty =", duty)
    time.sleep(1)  # 等待控制周期结束
    pwm.ChangeDutyCycle(0)  # 清空占空比，这句是防抖关键句，如果没有这句，舵机会狂抖不止

def servorShakeHead():
    global servor_degree_now
    temp = servor_degree_now
    setDirection(60)
    time.sleep(0.1)
    setDirection(120)
    time.sleep(0.1)
    setDirection(temp)
    
def setDirection360(direction):
    global pwm
    global servor_degree_now
    servor_degree_now = direction
    #if direction < 0 or direction > 360:
    #    return
    duty = (1/36) * direction + 2.5   # 将角度转换为占空比
    pwm.ChangeDutyCycle(duty)         # 改变PWM占空比
    time.sleep(0.02)
    print("direction =", direction, "-> duty =", duty)
    time.sleep(1)  # 等待控制周期结束
    pwm.ChangeDutyCycle(0)  # 清空占空比，这句是防抖关键句，如果没有这句，舵机会狂抖不止


def waitForInput():
    while True:
        try:
            val = input("input value to change direction(0~180): ").strip()
            if val:
                val = int(val)
                setDirection(val)
        except Exception as e:
            print("waitForInput: ", e)


captureCount = 0
def updateCaptureCount(count):
    screen.drawTextCN(5, 125, '捕获总数=', color='green', fontSize=18)
    screen.drawTextEN(85, 125, str(count), color='red', fontSize=18)
    
def screenShowInfo():
    screen.showInfo()
    updateCaptureCount(0)


def execCmd(cmd):
    cmd = cmd.strip()
    if cmd == 'help':
        content = "1. help: 获取所有可用指令\n" \
                  "2. servor <val>: 改变舵机的角度，输入范围:0~180\n" \
                  "3. capture: 摄像头截1张图\n" \
                  "4. screen on/off/show <msg>: LED屏幕开启/关闭/显示指定文字\n"
        socketClientSend('[msg] ' + content)

    elif cmd.startswith('admin'):  # super user commond
        opt, content = cmd.split(' ')[1:]  # option
        if opt == 'delCloudImg':
            deleteCloudImg(content)
            socketClientSend('[msg] success')

    elif cmd.startswith('servor'):
        val = cmd.split(' ')[-1]
        if val and val.isdigit():
            val = int(val)
            setDirection(val)
            socketClientSend('[msg] success')
        else:
            socketClientSend('[msg] error val')
    elif cmd.startswith('capture'):
        path = takeCapture()
        socketClientSend('[image] ' + path)
    elif cmd.startswith('screen'):
        val = cmd.split(' ')[1]
        if val == 'on':
            screen.openScreen()
        elif val == 'off':
            screen.closeScreen()
        elif val == 'show':
            msg = cmd.split(' ')[2:]
            if msg and len(msg):
                screen.clearScreen()
                screen.drawTextCN(5, 10, msg[0])
            else:
                screen.clearScreen()
                screenShowInfo()
            pass
        socketClientSend('[msg] success')
    else:
        socketClientSend('[msg] 指令不支持！输入 help 可查看所有指令！')


def waitForSocket():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', server_port))
    server.listen(1)
    print("Socket listen")
    while True:
        try:
            client, addr = server.accept()
            data = client.recv(max_transfer).decode('utf-8').strip()
            print("From Socket: ", data)
            client.close()
            execCmd(data)
        except Exception as e:
            print("waitForSocket: ", e)


def socketClientSend(msg):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(('localhost', client_port))
    client.send(msg.encode('utf-8'))
    client.close()


screen = None
servor_degree_now = 90
if __name__ == '__main__':
    if not os.path.exists('conf.json'):
        print(">> 缺少conf.json")
        exit()

    with open('conf.json', 'r+', encoding="utf-8-sig") as f:
        content = f.read()
    conf = json.loads(content)
    remote_server = conf['remote_server']
    client_port = conf['socket_server_port']  # 与qqListener正好相反
    server_port = conf['socket_client_port']
    max_transfer = conf['socket_max_transfer']
    qmsg_key = conf['qmsg_key']
    servor_pin = conf['servor_pin']


    GpioInit()
    setupSERVO()
    setDirection(90)

    screen = Screen()
    screen.initGPIO()
    screen.openScreen()
    screenShowInfo()
    try:
        th1 = Thread(target=waitForInput)
        th1.setDaemon(True)
        th1.start()

        th2 = Thread(target=waitForSocket)
        th2.setDaemon(True)
        th2.start()

        faces_video()
        while True:
            time.sleep(1)
    except Exception as e:
        print("main: ", e)
        GPIO.output(26, GPIO.LOW)  # LED
        GPIO.output(12, GPIO.LOW)  # BEEP
        setDirection(90)
    finally:
        GPIO.cleanup()


